Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9e99b8e
Replacing DependsOnMethod for initRouting() tests.
RobinTail Sep 14, 2025
0c802f0
Removing DependsOnMethod from example.
RobinTail Sep 14, 2025
f7aae44
Removing from Routing type and its support from walkRouting().
RobinTail Sep 14, 2025
e22992a
Removing exposition.
RobinTail Sep 14, 2025
e3cfa92
rm DependsOnMethod.
RobinTail Sep 14, 2025
da90e5f
Readme: adjusting documentation.
RobinTail Sep 14, 2025
6f5aeb4
Removing Routable, pulling members down to AbstractEndpoint.
RobinTail Sep 14, 2025
8fb3671
Adjusting names of the tests.
RobinTail Sep 14, 2025
b689972
Ref: minimizing the diff.
RobinTail Sep 14, 2025
b615de3
Merge branch 'make-v26' into rm-DependsOnMethod
RobinTail Sep 15, 2025
5004a7f
Merge branch 'make-v26' into rm-DependsOnMethod
RobinTail Sep 15, 2025
7d85911
Add basic migration for DependsOnMethod.
RobinTail Sep 15, 2025
4f8ac88
Migration for deprecated DependsOnMethod and one with nesting.
RobinTail Sep 16, 2025
47122b9
Merge branch 'make-v26' into rm-DependsOnMethod
RobinTail Sep 16, 2025
c5899e0
Changelog: add to v26.0.0 with example.
RobinTail Sep 16, 2025
fa53b07
Merge branch 'make-v26' into rm-DependsOnMethod
RobinTail Sep 17, 2025
ebd2a9e
Handling the case when object props are literals.
RobinTail Sep 17, 2025
3c08a0a
Handling the case of chain: both deprecated and having nesting.
RobinTail Sep 17, 2025
ec2af16
Apply suggestion from @RobinTail
RobinTail Sep 17, 2025
ba7951c
CR: avoiding mutation of the argument in nest() method.
RobinTail Sep 17, 2025
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
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@

### v26.0.0

- TODO: Add stuff here
- `DependsOnMethod` removed: use flat syntax with explicit method and a slash;

```patch
const routing: Routing = {
- "/v1/users": new DependsOnMethod({
+ "/v1/users": {
- get: getUserEndpoint,
+ "get /": getUserEndpoint,
- }).nest({
create: makeUserEndpoint
- }),
+ },
};
```

## Version 25

Expand Down
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ in one place, illustrating how you can structure your API using whichever method
architecture — or even mix them seamlessly.

```ts
import { Routing, DependsOnMethod, ServeStatic } from "express-zod-api";
import { Routing, ServeStatic } from "express-zod-api";

const routing: Routing = {
// flat syntax — /v1/users
Expand All @@ -298,12 +298,12 @@ const routing: Routing = {
// mixed syntax with explicit method — /v1/user/:id
"delete /user/:id": deleteUserEndpoint,
// method-based routing — /v1/account
account: new DependsOnMethod({
get: endpointA,
delete: endpointA,
post: endpointB,
patch: endpointB,
}),
account: {
"get /": endpointA,
"delete /": endpointA,
"post /": endpointB,
"patch /": endpointB,
},
},
// static file serving — /public serves files from ./assets
public: new ServeStatic("assets", {
Expand Down Expand Up @@ -1175,11 +1175,11 @@ new Documentation({
## Deprecated schemas and routes

As your API evolves, you may need to mark some parameters or routes as deprecated before deleting them. For this
purpose, the `.deprecated()` method is available on each schema, `Endpoint` and `DependsOnMethod`, it's immutable.
purpose, the `.deprecated()` method is available on each schema and `Endpoint`, it's immutable.
You can also deprecate all routes the `Endpoint` assigned to by setting `EndpointsFactory::build({ deprecated: true })`.

```ts
import { Routing, DependsOnMethod } from "express-zod-api";
import { Routing } from "express-zod-api";
import { z } from "zod";

const someEndpoint = factory.build({
Expand All @@ -1191,8 +1191,7 @@ const someEndpoint = factory.build({

const routing: Routing = {
v1: oldEndpoint.deprecated(), // deprecates the /v1 path
v2: new DependsOnMethod({ get: oldEndpoint }).deprecated(), // deprecates the /v2 path
v3: someEndpoint, // the path is assigned with initially deprecated endpoint (also deprecated)
v2: someEndpoint, // the path is assigned with initially deprecated endpoint (also deprecated)
};
```

Expand Down
12 changes: 6 additions & 6 deletions example/routing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DependsOnMethod, Routing, ServeStatic } from "express-zod-api";
import { Routing, ServeStatic } from "express-zod-api";
import { rawAcceptingEndpoint } from "./endpoints/accept-raw";
import { createUserEndpoint } from "./endpoints/create-user";
import { deleteUserEndpoint } from "./endpoints/delete-user";
Expand All @@ -16,12 +16,12 @@ export const routing: Routing = {
user: {
// syntax 1: methods are defined within the endpoint
retrieve: retrieveUserEndpoint, // path: /v1/user/retrieve
// syntax 2: methods are defined within the route (id is the route path param by the way)
":id": new DependsOnMethod({
patch: updateUserEndpoint, // demonstrates authentication
}).nest({
// id is the route path param
":id": {
remove: deleteUserEndpoint, // nested path: /v1/user/:id/remove
}),
// syntax 2: methods are defined within the route
"patch /": updateUserEndpoint, // demonstrates authentication
},
// demonstrates different response schemas depending on status code
create: createUserEndpoint,
// this one demonstrates the legacy array based response
Expand Down
34 changes: 0 additions & 34 deletions express-zod-api/src/depends-on-method.ts

This file was deleted.

10 changes: 8 additions & 2 deletions express-zod-api/src/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import { AbstractMiddleware, ExpressMiddleware } from "./middleware";
import { ContentType } from "./content-type";
import { ezRawBrand } from "./raw-schema";
import { DiscriminatedResult, pullResponseExamples } from "./result-helpers";
import { Routable } from "./routable";
import { AbstractResultHandler } from "./result-handler";
import type { Routing } from "./routing";
import { Security } from "./security";
import { ezUploadBrand } from "./upload-schema";

Expand All @@ -41,7 +41,13 @@ export type Handler<IN, OUT, OPT> = (params: {
logger: ActualLogger;
}) => Promise<OUT>;

export abstract class AbstractEndpoint extends Routable {
export abstract class AbstractEndpoint {
/** @desc Enables nested routes within the path assigned to the subject */
public nest(routing: Routing): Routing {
return Object.assign(routing, { "": this });
}
/** @desc Marks the route as deprecated (makes a copy of the endpoint) */
public abstract deprecated(): this;
public abstract execute(params: {
request: Request;
response: Response;
Expand Down
3 changes: 1 addition & 2 deletions express-zod-api/src/endpoints-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ interface BuildProps<
operationId?: string | ((method: ClientMethod) => string);
/**
* @desc HTTP method(s) this endpoint can handle
* @default "get" unless the Endpoint is assigned within DependsOnMethod
* @see DependsOnMethod
* @default "get" unless method is explicitly defined by Routing
* */
method?: Method | [Method, ...Method[]];
/**
Expand Down
1 change: 0 additions & 1 deletion express-zod-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export {
defaultResultHandler,
arrayResultHandler,
} from "./result-handler";
export { DependsOnMethod } from "./depends-on-method";
export { ServeStatic } from "./serve-static";
export { createServer, attachRouting } from "./server";
export { Documentation } from "./documentation";
Expand Down
11 changes: 0 additions & 11 deletions express-zod-api/src/routable.ts

This file was deleted.

8 changes: 0 additions & 8 deletions express-zod-api/src/routing-walker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { DependsOnMethod } from "./depends-on-method";
import { AbstractEndpoint } from "./endpoint";
import { RoutingError } from "./errors";
import { ClientMethod, isMethod, Method } from "./method";
Expand Down Expand Up @@ -98,13 +97,6 @@ export const walkRouting = ({
if (explicitMethod) prohibit(explicitMethod, path);
if (element instanceof ServeStatic) {
if (onStatic) element.apply(path, onStatic);
} else if (element instanceof DependsOnMethod) {
for (const [method, endpoint] of element.entries) {
const { methods } = endpoint;
checkDuplicate(method, path, visited);
checkMethodSupported(method, path, methods);
onEndpoint(method, path, endpoint);
}
} else {
stack.unshift(...processEntries(element, path));
}
Expand Down
5 changes: 2 additions & 3 deletions express-zod-api/src/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import createHttpError from "http-errors";
import { isProduction } from "./common-helpers";
import { CommonConfig } from "./config-type";
import { ContentType } from "./content-type";
import { DependsOnMethod } from "./depends-on-method";
import { Diagnostics } from "./diagnostics";
import { AbstractEndpoint } from "./endpoint";
import type { AbstractEndpoint } from "./endpoint";
import { CORSMethod, isMethod } from "./method";
import { OnEndpoint, walkRouting } from "./routing-walker";
import { ServeStatic } from "./serve-static";
Expand All @@ -19,7 +18,7 @@ import * as R from "ramda";
* @example { v1: { "patch /books/:bookId": changeBookEndpoint } }
* */
export interface Routing {
[K: string]: Routing | DependsOnMethod | AbstractEndpoint | ServeStatic;
[K: string]: Routing | AbstractEndpoint | ServeStatic;
}

export type Parsers = Partial<Record<ContentType, RequestHandler[]>>;
Expand Down
3 changes: 0 additions & 3 deletions express-zod-api/tests/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

exports[`Index Entrypoint > exports > BuiltinLogger should have certain value 1`] = `[Function]`;

exports[`Index Entrypoint > exports > DependsOnMethod should have certain value 1`] = `[Function]`;

exports[`Index Entrypoint > exports > Documentation should have certain value 1`] = `[Function]`;

exports[`Index Entrypoint > exports > DocumentationError should have certain value 1`] = `[Function]`;
Expand Down Expand Up @@ -84,7 +82,6 @@ exports[`Index Entrypoint > exports > should have certain entities exposed 1`] =
"ResultHandler",
"defaultResultHandler",
"arrayResultHandler",
"DependsOnMethod",
"ServeStatic",
"createServer",
"attachRouting",
Expand Down
18 changes: 9 additions & 9 deletions express-zod-api/tests/__snapshots__/routing.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ RoutingError({
})
`;

exports[`Routing > initRouting() > Should check if endpoint supports the method it's assigned to within DependsOnMethod 1`] = `
exports[`Routing > initRouting() > Should check if endpoint supports the method it's assigned to 1`] = `
RoutingError({
"cause": {
"method": "post",
Expand All @@ -20,7 +20,7 @@ RoutingError({
})
`;

exports[`Routing > initRouting() > Should prohibit DependsOnMethod for a route having explicit method 1`] = `
exports[`Routing > initRouting() > Should prohibit ServeStatic for a route having explicit method 1`] = `
RoutingError({
"cause": {
"method": "get",
Expand All @@ -30,27 +30,27 @@ RoutingError({
})
`;

exports[`Routing > initRouting() > Should prohibit ServeStatic for a route having explicit method 1`] = `
exports[`Routing > initRouting() > Should prohibit duplicated routes 1`] = `
RoutingError({
"cause": {
"method": "get",
"path": "/v1/user/retrieve",
"path": "/v1/test",
},
"message": "Route with explicit method can only be assigned with Endpoint",
"message": "Route has a duplicate",
})
`;

exports[`Routing > initRouting() > Should prohibit duplicated routes 1`] = `
exports[`Routing > initRouting() > Should prohibit nested routing within a route having explicit method 1`] = `
RoutingError({
"cause": {
"method": "get",
"path": "/v1/test",
"path": "/v1/user/retrieve",
},
"message": "Route has a duplicate",
"message": "Route with explicit method can only be assigned with Endpoint",
})
`;

exports[`Routing > initRouting() > Should prohibit nested routing within a route having explicit method 1`] = `
exports[`Routing > initRouting() > Should prohibit nesting for a route having explicit method 1`] = `
RoutingError({
"cause": {
"method": "get",
Expand Down
64 changes: 0 additions & 64 deletions express-zod-api/tests/depends-on-method.spec.ts

This file was deleted.

14 changes: 14 additions & 0 deletions express-zod-api/tests/endpoint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ describe("Endpoint", () => {
});
});

describe(".nest()", () => {
test("should return Routing arrangement", () => {
const subject = defaultEndpointsFactory.build({
output: z.object({}),
handler: vi.fn(),
});
expect(subject).toHaveProperty("nest", expect.any(Function));
expect(subject.nest({ subpath: subject })).toEqual({
"": subject,
subpath: subject,
});
});
});

describe("#parseOutput", () => {
test("Should throw on output validation failure", async () => {
const endpoint = defaultEndpointsFactory.build({
Expand Down
Loading