Skip to content

feat!: replace decoration with options-based dependency injection#20

Merged
johnf merged 2 commits intomainfrom
refactor/drop-fastify-plugin-wrapper
Apr 29, 2026
Merged

feat!: replace decoration with options-based dependency injection#20
johnf merged 2 commits intomainfrom
refactor/drop-fastify-plugin-wrapper

Conversation

@johnf
Copy link
Copy Markdown
Collaborator

@johnf johnf commented Apr 24, 2026

Summary

Removes the fastify-plugin wrapper and switches arocapi to an encapsulated Fastify plugin. Internal routes now receive prisma (and opensearch where needed) explicitly via their own options instead of reading from decorated fastify.prisma/fastify.opensearch. Connection lifecycle management moves from arocapi to the consumer.

Why

The previous design globally augmented every consumer's FastifyInstance type with prisma and opensearch fields, and arocapi assumed ownership of $connect/$disconnect/ping/close on clients it didn't create. Both are leaky abstractions for a library: consumer types shouldn't be polluted with arocapi's internal dependencies, and lifecycle should stay with whoever constructed the client.

Breaking changes (→ v3.0.0)

  • fastify.prisma and fastify.opensearch are no longer decorated onto the consumer's Fastify instance. Consumers close over their own client references.
  • TransformerContext and FileHandlerContext shrink from { request, fastify } to { request }.
  • Arocapi no longer calls prisma.$connect(), prisma.$disconnect(), opensearch.ping(), or opensearch.close(). Consumers connect before register() and close on their own shutdown path.
  • The fastify-plugin package is no longer a dependency.

What changed

  • src/app.ts — drop fp() wrapper, drop declare module 'fastify' augmentation, delete setupDatabase/setupSearch, pass prisma/opensearch explicitly to each child route plugin
  • src/routes/*.ts (6 files) — each gains a prisma: PrismaClient (and opensearch: Client for search) on its options type
  • src/types/transformers.ts, src/types/fileHandlers.ts — context types reduced to { request }
  • src/test/helpers/fastify.ts — stops decorating the fastify instance with mocks
  • src/routes/*.test.ts — pass prisma/opensearch to register() explicitly
  • src/app.test.ts — drop should handle broken opensearch (tested the removed ping()) and should handle random errors (tested fp()-leaked cross-scope error handling). Add should register successfully with all required options to preserve happy-path registration coverage.
  • src/index.dev.ts — remove vestigial declare module block and unused { request, fastify } destructuring in the example transformer
  • README.md — transformer examples now close over the consumer's own prisma reference
  • package.json — remove fastify-plugin

Test plan

  • pnpm lint — biome, tsc, knip all green
  • pnpm test — 132/132 passing, 100% coverage across lines/branches/functions/statements
  • Verify semantic-release cuts a v3.0.0 major from the BREAKING CHANGE: footer when this merges

Removes the fastify-plugin wrapper and shifts arocapi to an
encapsulated Fastify plugin. Internal routes now receive prisma
(and opensearch where needed) via their own plugin options rather
than via a decorated FastifyInstance. Lifecycle management of the
passed-in clients moves to the consumer, aligning with
"whoever creates owns the lifecycle".

BREAKING CHANGE: arocapi no longer decorates fastify.prisma or
fastify.opensearch; consumers must close over their own client
references. TransformerContext and FileHandlerContext shrink from
{ request, fastify } to { request }. Arocapi no longer calls
prisma.$connect()/$disconnect() or opensearch.ping()/close() —
consumers now own connecting before registration and closing on
shutdown.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 24, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 100% (🎯 100%) 301 / 301
🔵 Statements 100% (🎯 100%) 312 / 312
🔵 Functions 100% (🎯 100%) 48 / 48
🔵 Branches 100% (🎯 100%) 148 / 148
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/app.ts 100% 100% 100% 100%
src/routes/crate.ts 100% 100% 100% 100%
src/routes/entities.ts 100% 100% 100% 100%
src/routes/entity.ts 100% 100% 100% 100%
src/routes/file.ts 100% 100% 100% 100%
src/routes/files.ts 100% 100% 100% 100%
src/routes/search.ts 100% 100% 100% 100%
Generated in workflow #72 for commit 694b41b by the Vitest Coverage Report Action

Copy link
Copy Markdown
Member

@alvinsw alvinsw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the fastify instance passed to the transformers and handlers gives more flexibility to the users extending the arocapi. Is there any drawbacks if we still do that?

Comment thread src/app.ts
Restores the `fastify: FastifyInstance` field on both context types so
consumer-supplied transformers and file handlers can access helpers
that the consumer has decorated onto fastify themselves.

Arocapi still does not decorate fastify with prisma/opensearch (the
v3 breaking change in the previous commit stands). The field is
purely an extension point for consumers; arocapi puts nothing on it.
@johnf
Copy link
Copy Markdown
Collaborator Author

johnf commented Apr 29, 2026

Having the fastify instance passed to the transformers and handlers gives more flexibility to the users extending the arocapi. Is there any drawbacks if we still do that?

@alvinsw I've pushed a commit that brings it back.

Comment thread src/routes/files.ts
const where: NonNullable<Parameters<typeof prisma.file.findMany>[0]>['where'] = {};

if (memberOf) {
where.entity = { id: memberOf };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
where.entity = { id: memberOf };
where.entity = { memberOf };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just fix this one annoying bug here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that separately as another PR now.

@johnf johnf merged commit aa5c6b7 into main Apr 29, 2026
1 check passed
@johnf johnf deleted the refactor/drop-fastify-plugin-wrapper branch April 29, 2026 04:54
github-actions Bot pushed a commit that referenced this pull request Apr 29, 2026
## [4.0.0](v3.0.0...v4.0.0) (2026-04-29)

### ⚠ BREAKING CHANGES

* arocapi no longer decorates fastify.prisma or
fastify.opensearch; consumers must close over their own client
references.  Arocapi no longer calls
prisma.$connect()/$disconnect() or opensearch.ping()/close() —
consumers now own connecting before registration and closing on
shutdown.

### Features

* replace decoration with options-based dependency injection ([#20](#20)) ([aa5c6b7](aa5c6b7))

### Bug Fixes

* **files:** filter by entity.memberOf instead of entity.id ([#21](#21)) ([a6f27bd](a6f27bd))
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 4.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants