jlalmes / trpc-openapi

OpenAPI support for tRPC 🧩
https://www.npmjs.com/package/trpc-openapi
MIT License
2.22k stars 153 forks source link

TRPCError: Input parser must be a ZodObject #204

Closed dawoodaijaz97 closed 1 year ago

dawoodaijaz97 commented 1 year ago

Hi, I am getting the following error: TRPCError: Input parser must be a ZodObject. I am following the nextjs example provided in trpc-openapi/src/examples. Getting the same error on all trpc-open api routes [...trpc] including the openapi.json route.

I have looked through all the previous issues and can't seem to figure out what I am doing wrong.

Please let me know if additional details are required.

Regards, Dawood

Package.json:

{ "name": "next-boilerplate", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@tanstack/react-query": "^4.20.2", "@trpc/client": "^10.5.0", "@trpc/next": "^10.5.0", "@trpc/react-query": "^10.5.0", "@trpc/server": "^10.5.0", "@types/styled-components-react-native": "^5.1.3", "next": "12.1.6", "nextjs-cors": "^2.1.2", "react": "18.1.0", "react-dom": "18.1.0", "react-is": "^18.2.0", "styled-components": "^5.3.5", "superjson": "^1.12.0", "swagger-ui-react": "^4.15.5", "trpc-openapi": "^1.0.0", "zod": "^3.20.2" }, "devDependencies": { "@types/node": "17.0.36", "@types/react": "18.0.9", "@types/react-dom": "18.0.5", "@types/styled-components": "^5.1.25", "@types/swagger-ui-react": "^4.11.0", "eslint": "8.16.0", "eslint-config-next": "12.1.6", "typescript": "4.7.2" }, "resolutions": { "styled-components": "^5" } }

jlalmes commented 1 year ago

Please share the full error & procedure that is causing the error.

dawoodaijaz97 commented 1 year ago

---------------pages/api/[...trpc].ts-----------------

import { NextApiRequest, NextApiResponse } from 'next';
import cors from 'nextjs-cors';
import { createOpenApiNextHandler } from 'trpc-openapi';

import { appRouter} from '../../server/routers/_app';
import {createContext} from '../../server/context';

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
    await cors(req, res);
    return createOpenApiNextHandler({
        router: appRouter,
        createContext,
    })(req, res);
};

export default handler;

------------pages/api/openapi.json.ts------------

import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import 'swagger-ui-react/swagger-ui.css';

const SwaggerUI = dynamic(() => {
    return import("swagger-ui-react");
}, { ssr: false });

const OpenAPIPage: NextPage = () => {
    // Serve Swagger UI with our OpenAPI schema
    return <SwaggerUI url="/api/openapi.json" />;
};

export default OpenAPIPage;

------------/pages/api/openapi.tsx------------

import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import 'swagger-ui-react/swagger-ui.css';

const SwaggerUI = dynamic(() => {
    return import("swagger-ui-react");
}, { ssr: false });

const OpenAPIPage: NextPage = () => {
    // Serve Swagger UI with our OpenAPI schema
    return <SwaggerUI url="/api/openapi.json" />;
};

export default OpenAPIPage;

--------------/server/routers/_app.ts---------------

import {mergeRouters, publicProcedure, router} from '../init';
import {userRouter} from "./user";
import {testRouter} from "./test";

export const appRouter = router({
    health: publicProcedure.query(() => 'ok'),
    user: userRouter,
    test: testRouter,
});

export type AppRouter = typeof appRouter;

-------------/server/routers/user.ts---------------


import {publicProcedure, router} from '../init';

import z from 'zod';
import {User} from "../models/User.model";

export const userRouter = router({
    listUsers: publicProcedure
        .meta({
        openapi:{
            method: 'GET',
            path: '/users/listUsers',
            tags: ['users'],
            summary: 'Read all users',
        }
    })
        .input(z.void())
        .output(z.array(z.any()))
        .query(async () => {
            console.log('listUsers');
            return User.find({});
        })
});

image

jlalmes commented 1 year ago

Lol you've shared everything except the testRouter where the error is occurring!

dawoodaijaz97 commented 1 year ago

I have 2 routes, user and test. When i make request to to any route /api/* except api/trpc/ all the requests are going to test route. In the image I shared the requested URL is /user/ListUsers but the request went to test Route.

Here is the code for the test route

import { publicProcedure, router } from '../init';
import z from "zod";
export const testRouter = router({
    testDBConnection: publicProcedure
        .meta({ openapi: { method: 'GET', path: '/testDBConnection' } })
        .input(z.object({ name: z.string()}).nullish())
        .query(() => {
        console.log('Test DB Connection');
    }),
});

Below is my folder setup for TRPC and Next routes

image

jlalmes commented 1 year ago

This testDBConnection procedure is causing the error - it is because your input parser can be null | undefined, but it must be an object. Please see the following possible change:

- .input(z.object({ name: z.string() }).nullish())
+ .input(z.object({ name: z.string().nullish() }))
dawoodaijaz97 commented 1 year ago

Thanks @jlalmes It worked.