Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: injected services should not be re-evaluated within the request scope #12

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/dependency.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,27 @@ describe("Dependency", () => {
expect(createCount).toBe(2);
});

it("injected service will not be re-evaluated on request scope", async () => {
const dep = new Dependency(() => new TestService("foo"), {
scope: "request",
});
const app = new Hono()
.use(dep.middleware("service"))
.get("/", (c) => c.text(c.var.service.name));

dep.injection(new TestService("bar"));

const res1 = await app.request("/");
expect(res1.status).toBe(200);
const data1 = await res1.text();
expect(data1).toBe("bar");

const res2 = await app.request("/");
expect(res2.status).toBe(200);
const data2 = await res2.text();
expect(data2).toBe("bar");
});

it("clear injected service", async () => {
const dep = new Dependency(() => new TestService("foo"));
const app = new Hono()
Expand Down
16 changes: 10 additions & 6 deletions src/dependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,42 @@ export class Dependency<Service> {
},
) {}

// service cache
private service?: Service;
private serviceInjected?: Service;
private serviceCached?: Service;
// used as a basis for caching when evaluating the request scope
private lastRequest?: Request;

/**
* Injects a service instance directly. Useful for overriding the default service.
*/
injection(service: Service): this {
this.service = service;
this.serviceInjected = service;
return this;
}

/**
* Clear injected service.
*/
clearInjected(): this {
this.service = undefined;
this.serviceInjected = undefined;
return this;
}

/**
* Resolve service.
*/
async resolve(c: Context): Promise<Service> {
if (this.serviceInjected) {
return this.serviceInjected;
}

// evaluate and clear the cache when handling the request scope
if (this.opts?.scope === "request" && this.lastRequest !== c.req.raw) {
this.service = undefined;
this.serviceCached = undefined;
this.lastRequest = c.req.raw;
}

const service = (this.service ??= await this.serviceInitializer(c));
const service = (this.serviceCached ??= await this.serviceInitializer(c));

return service;
}
Expand Down