Skip to content

Commit

Permalink
Extend id params schemas (#3)
Browse files Browse the repository at this point in the history
Added id params for uuid and slug

---------

Co-authored-by: Steven Rafferty <[email protected]>
  • Loading branch information
w3cj and npmSteven authored Oct 14, 2024
1 parent 37f48e6 commit c58a8a1
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 3 deletions.
92 changes: 90 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ To see real world usage of these utilities, checkout the [hono-open-api-starter
- [Schemas](#schemas)
- [stoker/openapi/schemas/id-params](#stokeropenapischemasid-params)
- [Example Usage](#example-usage-9)
- [stoker/openapi/schemas/create-message-object](#stokeropenapischemascreate-message-object)
- [stoker/openapi/schemas/slug-params](#stokeropenapischemasslug-params)
- [Example Usage](#example-usage-10)
- [stoker/openapi/schemas/create-error-schema](#stokeropenapischemascreate-error-schema)
- [stoker/openapi/schemas/id-uuid-params](#stokeropenapischemasid-uuid-params)
- [Example Usage](#example-usage-11)
- [stoker/openapi/schemas/create-message-object](#stokeropenapischemascreate-message-object)
- [Example Usage](#example-usage-12)
- [stoker/openapi/schemas/create-error-schema](#stokeropenapischemascreate-error-schema)
- [Example Usage](#example-usage-13)
- [Credits](#credits)

## Utilities
Expand Down Expand Up @@ -379,6 +383,90 @@ app.openapi(
export default app;
```

#### stoker/openapi/schemas/slug-params

Validate `slug` in path params as a slug.

##### Example Usage

```ts
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import * as HttpStatusCodes from "stoker/http-status-codes";
import jsonContent from "stoker/openapi/helpers/json-content";
import SlugParamsSchema from "stoker/openapi/schemas/slug-params";

const app = new OpenAPIHono();

app.openapi(
createRoute({
method: "get",
path: "/posts/{slug}",
request: {
params: SlugParamsSchema,
},
responses: {
[HttpStatusCodes.OK]: jsonContent(
z.object({
slug: z.string(),
}),
"Retrieve the post",
),
},
}),
(c) => {
// slug is a valid slug
const { slug } = c.req.valid("param");
return c.json({
slug,
}, HttpStatusCodes.OK);
},
);

export default app;
```

#### stoker/openapi/schemas/id-uuid-params

Validate `id` in path params as a uuid.

##### Example Usage

```ts
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import * as HttpStatusCodes from "stoker/http-status-codes";
import jsonContent from "stoker/openapi/helpers/json-content";
import IdUUIDParamsSchema from "stoker/openapi/schemas/id-uuid-params";

const app = new OpenAPIHono();

app.openapi(
createRoute({
method: "get",
path: "/users/{id}",
request: {
params: IdUUIDParamsSchema,
},
responses: {
[HttpStatusCodes.OK]: jsonContent(
z.object({
id: z.uuid(),
}),
"Retrieve the user",
),
},
}),
(c) => {
// id is a valid uuid
const { id } = c.req.valid("param");
return c.json({
id,
}, HttpStatusCodes.OK);
},
);

export default app;
```

#### stoker/openapi/schemas/create-message-object

Create an object schema with a message string property. Useful for error messages.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "stoker",
"type": "module",
"version": "1.0.9",
"version": "1.1.0",
"packageManager": "[email protected]",
"description": "Utilities for hono and @hono/zod-openapi",
"author": "w3cj <[email protected]>",
Expand Down
14 changes: 14 additions & 0 deletions src/openapi/schemas/id-uuid-params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from "@hono/zod-openapi";

const IdUUIDParamsSchema = z.object({
id: z.string().uuid().openapi({
param: {
name: "id",
in: "path",
},
required: ["id"],
example: "4651e634-a530-4484-9b09-9616a28f35e3",
}),
});

export default IdUUIDParamsSchema;
60 changes: 60 additions & 0 deletions src/openapi/schemas/slug-params.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, expect, it } from "vitest";

import SlugParamsSchema from "./slug-params";

describe("slug-params", () => {
it("allows letters", () => {
const slug = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const { data, error } = SlugParamsSchema.safeParse({
slug,
});
expect(data?.slug).toBe(slug);
expect(error).toBeUndefined();
});
it("allows numbers", () => {
const slug = "0123456789";
const { data, error } = SlugParamsSchema.safeParse({
slug,
});
expect(data?.slug).toBe(slug);
expect(error).toBeUndefined();
});
it("allows numbers and letters", () => {
const slug = "0123456789abcdeABCDE";
const { data, error } = SlugParamsSchema.safeParse({
slug,
});
expect(data?.slug).toBe(slug);
expect(error).toBeUndefined();
});
it("allows dashes", () => {
const slug = "test-slug-here";
const { data, error } = SlugParamsSchema.safeParse({
slug,
});
expect(data?.slug).toBe(slug);
expect(error).toBeUndefined();
});
it("allows underscores", () => {
const slug = "test_slug_here";
const { data, error } = SlugParamsSchema.safeParse({
slug,
});
expect(data?.slug).toBe(slug);
expect(error).toBeUndefined();
});
it("does not allow special characters only", () => {
const slug = "!@#$%^&*()+-=";
const { error } = SlugParamsSchema.safeParse({
slug,
});
expect(error).toBeDefined();
});
it("does not allow special characters with allowed characters", () => {
const slug = "abc-DEF_ABC-!@#$%^&*()+-=";
const { error } = SlugParamsSchema.safeParse({
slug,
});
expect(error).toBeDefined();
});
});
20 changes: 20 additions & 0 deletions src/openapi/schemas/slug-params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { z } from "@hono/zod-openapi";

// Regular expression to validate slug format: alphanumeric, underscores, and dashes
const slugReg = /^[\w-]+$/;
const SLUG_ERROR_MESSAGE = "Slug can only contain letters, numbers, dashes, and underscores";

const SlugParamsSchema = z.object({
slug: z.string()
.regex(slugReg, SLUG_ERROR_MESSAGE)
.openapi({
param: {
name: "slug",
in: "path",
},
required: ["slug"],
example: "my-cool-article",
}),
});

export default SlugParamsSchema;

0 comments on commit c58a8a1

Please sign in to comment.