Add OpenAPI documentation with Swagger UI for tRPC API#49
Add OpenAPI documentation with Swagger UI for tRPC API#49
Conversation
|
Important Review skippedBot user detected. To trigger a single review, invoke the You can disable this status message by setting the Comment |
Co-authored-by: SimonLoir <17649337+SimonLoir@users.noreply.github.com>
Co-authored-by: SimonLoir <17649337+SimonLoir@users.noreply.github.com>
Co-authored-by: SimonLoir <17649337+SimonLoir@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive OpenAPI 3.1 documentation for the tRPC API with an interactive Swagger UI interface at /api-docs. It includes field-level documentation for all Zod schemas and a manually maintained static OpenAPI specification document (chosen over auto-generation due to superjson transformation and Prisma type complexity).
- Added
.describe()calls to all Zod schemas across 10 schema files for field-level documentation - Created static OpenAPI 3.1 specification in
packages/api/src/openapi.tsdocumenting all endpoints with tRPC dot-path convention - Implemented Swagger UI page at
/api-docswith dynamic JavaScript and CSS loading fromswagger-ui-distpackage
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Added dependencies: swagger-ui-dist@5.30.3 for UI and openapi3-ts@4.5.0 for TypeScript types |
| packages/schemas/src/volumes.ts | Added descriptions to volume schemas including volumeId, volume name, mount path, and readOnly fields |
| packages/schemas/src/sshKeys.ts | Added descriptions to SSH key schemas including key creation, identification, and update operations |
| packages/schemas/src/services.ts | Added descriptions to service schemas for Docker Swarm service management and network operations |
| packages/schemas/src/servers.ts | Added descriptions to server schemas for SSH connection configuration |
| packages/schemas/src/registries.ts | Added descriptions to container registry schemas for authentication and registry management |
| packages/schemas/src/projects.ts | Added descriptions to project schemas for project identification and creation |
| packages/schemas/src/notifications.ts | Added descriptions to notification provider schemas including Discord webhooks |
| packages/schemas/src/networks.ts | Added descriptions to Docker network schemas including driver types, subnet, and gateway configuration |
| packages/schemas/src/environment-variables.ts | Added descriptions for environment variable schemas in KEY=VALUE format |
| packages/schemas/src/domains.ts | Added descriptions to domain routing schemas including FQDN validation and port configuration |
| packages/schemas/src/deployments.ts | Added description to deployment identification schema |
| packages/api/src/openapi.ts | Created comprehensive static OpenAPI 3.1 document with 40+ endpoint definitions using tRPC conventions |
| packages/api/src/index.ts | Exported openApiDocument for use in web application |
| packages/api/package.json | Added openapi3-ts dependency for OpenAPI TypeScript types |
| apps/web/package.json | Added swagger-ui-dist dependency for Swagger UI assets |
| apps/web/app/api/docs/openapi.json/route.ts | Created Next.js API route to serve OpenAPI JSON document |
| apps/web/app/api-docs/page.tsx | Implemented client-side Swagger UI page with dynamic asset loading |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| link.href = | ||
| '/_next/static/css/swagger-ui.css'; |
There was a problem hiding this comment.
The CSS file path /_next/static/css/swagger-ui.css appears to be incorrect. The swagger-ui-dist package provides CSS files that should be accessed through the node_modules path or copied to the public directory. Next.js won't automatically serve CSS from swagger-ui-dist at this path.
Consider using the CSS directly from the package using an import statement or copying the CSS file to the public directory and referencing it properly. For example:
import 'swagger-ui-dist/swagger-ui.css';Or serve it from a public asset path if you copy swagger-ui.css from the package to your public directory.
| )} | ||
| <div ref={containerRef} /> | ||
| <style jsx global>{` | ||
| /* Swagger UI CSS is loaded via the JS bundle */ |
There was a problem hiding this comment.
The comment on line 65 mentions "Swagger UI CSS is loaded via the JS bundle", but this is misleading. The CSS is actually being loaded dynamically by manually creating a link element (lines 22-26), not through the JS bundle import. The global styles defined here only add minor adjustments to the Swagger UI styling.
Consider updating the comment to be more accurate:
/* Additional styling adjustments for Swagger UI */| /* Swagger UI CSS is loaded via the JS bundle */ | |
| /* Additional styling adjustments for Swagger UI */ |
| '/registries.update': { | ||
| post: { | ||
| tags: ['Registries'], | ||
| summary: 'Update a container registry', | ||
| description: 'Updates an existing container registry configuration.', | ||
| security: [{ cookieAuth: [] }], | ||
| responses: { | ||
| '200': { description: 'Updated registry' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including registryId and other fields from updateRegistrySchema. Users won't know what parameters to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['registryId', 'name', 'username', 'password', 'url'],
properties: {
registryId: { type: 'string', description: 'Unique identifier of the registry' },
name: { type: 'string', description: 'Display name for the container registry' },
username: { type: 'string', description: 'Username for registry authentication' },
password: { type: 'string', description: 'Password or access token for registry authentication' },
url: { type: 'string', description: 'URL of the container registry' },
},
},
},
},
},| '/services.deleteService': { | ||
| post: { | ||
| tags: ['Services'], | ||
| summary: 'Delete a service', | ||
| description: 'Permanently deletes a service and its configurations.', | ||
| security: [{ cookieAuth: [] }], | ||
| responses: { | ||
| '200': { description: 'Deletion result' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including serviceId. Users won't know what parameter to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['serviceId'],
properties: {
serviceId: { type: 'string', description: 'Unique identifier of the service' },
},
},
},
},
},| '/services.deployService': { | ||
| post: { | ||
| tags: ['Services'], | ||
| summary: 'Deploy a service', | ||
| description: 'Deploys the service to its configured server. Returns true if successful.', | ||
| security: [{ cookieAuth: [] }], | ||
| responses: { | ||
| '200': { description: 'Deployment result' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including serviceId. Users won't know what parameter to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['serviceId'],
properties: {
serviceId: { type: 'string', description: 'Unique identifier of the service' },
},
},
},
},
},| '/registries.delete': { | ||
| post: { | ||
| tags: ['Registries'], | ||
| summary: 'Delete a container registry', | ||
| description: 'Permanently deletes a container registry from the organization.', | ||
| security: [{ cookieAuth: [] }], | ||
| responses: { | ||
| '200': { description: 'Deletion result' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including registryId. Users won't know what parameter to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['registryId'],
properties: {
registryId: { type: 'string', description: 'Unique identifier of the registry' },
},
},
},
},
},| responses: { | ||
| '200': { description: 'Deletion result' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including projectId. Users won't know what parameter to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['projectId'],
properties: {
projectId: { type: 'string', description: 'Unique identifier of the project' },
},
},
},
},
},| responses: { | |
| '200': { description: 'Deletion result' }, | |
| '401': { description: 'Unauthorized' }, | |
| }, | |
| }, | |
| requestBody: { | |
| required: true, | |
| content: { | |
| 'application/json': { | |
| schema: { | |
| type: 'object', | |
| required: ['projectId'], | |
| properties: { | |
| projectId: { type: 'string', description: 'Unique identifier of the project' }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| responses: { | |
| '200': { description: 'Deletion result' }, | |
| '401': { description: 'Unauthorized' }, | |
| }, |
| '/domains.delete': { | ||
| post: { | ||
| tags: ['Domains'], | ||
| summary: 'Delete a domain', | ||
| description: 'Permanently deletes a domain mapping from a service.', | ||
| security: [{ cookieAuth: [] }], | ||
| responses: { | ||
| '200': { description: 'Deletion result' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including domainId. Users won't know what parameter to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['domainId'],
properties: {
domainId: { type: 'string', description: 'Unique identifier of the domain' },
},
},
},
},
},| '/networks.delete': { | ||
| post: { | ||
| tags: ['Networks'], | ||
| summary: 'Delete a network', | ||
| description: 'Permanently deletes a Docker network.', | ||
| security: [{ cookieAuth: [] }], | ||
| responses: { | ||
| '200': { description: 'Deletion result' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including networkId. Users won't know what parameter to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['networkId'],
properties: {
networkId: { type: 'string', description: 'Unique identifier of the network' },
},
},
},
},
},| '/volumes.update': { | ||
| post: { | ||
| tags: ['Volumes'], | ||
| summary: 'Update a volume', | ||
| description: 'Updates an existing volume configuration.', | ||
| security: [{ cookieAuth: [] }], | ||
| responses: { | ||
| '200': { description: 'Updated volume' }, | ||
| '401': { description: 'Unauthorized' }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Missing requestBody specification. This POST endpoint needs to specify the expected request body schema including volumeId and optional fields from updateVolumeSchema. Users won't know what parameters to send without this documentation.
Add the requestBody definition:
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['volumeId'],
properties: {
volumeId: { type: 'string', description: 'Unique identifier of the volume' },
name: { type: 'string', description: 'Docker volume name (alphanumeric, hyphens, underscores, periods)' },
mountPath: { type: 'string', description: 'Absolute path inside the container where the volume will be mounted' },
readOnly: { type: 'boolean', description: 'Whether the volume is read-only' },
},
},
},
},
},
Adds OpenAPI 3.1 documentation for the tRPC API with an interactive Swagger UI at
/api-docs.Schema Documentation
.describe()to all Zod schemas inpackages/schemas/src/for field-level API documentationOpenAPI Specification
packages/api/src/openapi.tsdocumenting all endpoints/health.ping,/sshKeys.list)Swagger UI
/api-docsusingswagger-ui-dist/api/docs/openapi.jsonNotes
Used static OpenAPI document rather than
trpc-to-openapidynamic generation because tRPC uses superjson transformer and Prisma return types are complex to express as Zod output schemas.Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
checkpoint.prisma.io/usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/sea-stack-app/sea-stack-app/node_modules/.pnpm/prisma@6.19.0_typescript@5.9.3/node_modules/prisma/build/child {"product":"prisma","version":"6.19.0","cli_install_type":"local","information":"","local_timestamp":"2025-11-27T02:17:20Z","project_hash":"c1ed741a","cli_path":"/home/REDACTED/work/sea-stack-app/sea-stack-app/packages/db/node_modules/prisma/build/index.js"(dns block)/usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/sea-stack-app/sea-stack-app/node_modules/.pnpm/prisma@6.19.0_typescript@5.9.3/node_modules/prisma/build/child {"product":"prisma","version":"6.19.0","cli_install_type":"local","information":"","local_timestamp":"2025-11-27T02:37:05Z","project_hash":"c1ed741a","cli_path":"/home/REDACTED/work/sea-stack-app/sea-stack-app/packages/db/node_modules/prisma/build/index.js"(dns block)https://api.github.com//advisories/home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js(http block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.