Search code examples
reactjstypescriptexpressgraphqlapollo-server

Displaying pure JSON format with black background in GraphQL Express React


I'm having issue of displaying the response of the API. It appears white like this 1

Which is should be similar like this 2

My code in the backend server:

main.ts

import { typeDefs, resolvers } from './src/graphql/Resolver'

AppDataSource.initialize()
const app = express()
const httpServer = http.createServer(app)
const server = new ApolloServer({
    typeDefs,
    resolvers,
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })]
});
(async () => {
    await server.start()
    app.use(
        '/gql',
        cors<cors.CorsRequest>({ origin: `http://${process.env.DOMAIN}:${process.env.CLIENT_PORT}`, credentials: true }),
        express.json(),
        cp(),
        expressMiddleware(server, {
            context: async ({ req, res }) => ({ req, res })
        })
    )
    httpServer.listen(process.env.PORT)
})()

Resolver.ts

import API from './resolver/api/Data'
import Books from './resolver/api/Books'

export const typeDefs = Schema
export const resolvers = {
    API: {
        __resolveType(obj: API) {
            if ((obj as Data).user_id) return 'Data'
            else return 'Error'
        }
    },
    Data: {
        books: Books
    },
    Query: {
        api: API
    }
}

Data.ts

const Data = async (_: null, args: { api_key: string }) => {
    try {
        const userRepo = AppDataSource.getRepository(User)
        const { api_key } = args
        const hashBuffer = Buffer.from(api_key, 'hex')
        const user = await userRepo.findOne({ where: { api_key: hashBuffer } })
        if (!user) return { message: 'Invalid API Key!'}
        return {
            user_id: user.user_id,
            email: user.email,
            username: user.username
        }
    } catch {
        throw new GraphQLError('Internal Server Error', { extensions: { code: '500' } })
    }
}
export default Data

Books.ts (the parent refers to Data)

const Books = async (parent: { user_id: number }) => {
    try {
        const bookRepo = AppDataSource.getRepository(Book)
        const books = await bookRepo.find({ where: { user_id: parent!.user_id } })
        return books.map(book => ({
            cover_i: book.cover_i,
            isbn: book.isbn,
            title: book.title,
            author_name: book.author_name
        }))
    } catch {
        throw new GraphQLError('Internal Server Error', { extensions: { code: '500' } })
    }
}
export default Books

Frontend:

App.tsx

const App: React.FC = () => {
    const { loading, data, error } = useQuery(AuthGQL)
    const dispatch = useDispatch()
    React.useEffect(() => {
        if (!loading) {
            if (data) dispatch(setUser(data.auth))
            else if (error) dispatch(setUser(null))
        }
    }, [data, error])
    return (
        <BrowserRouter>
            <main>
                <Routes>
                    <Route path="API/:hash" element={<API />} />
                </Routes>
            </main>
        </BrowserRouter>
    )
}
export default App

API.tsx

interface Book {
    cover_i: string
    isbn: string
    title: string
    author_name: string
    __typename: string
}
const API: React.FC = () => {
    const { hash } = useParams<{ hash: string }>()
    const { loading, data, error } = useQuery(APIGQL, { variables: { api_key: hash } })
    const apiState = useSelector((state: RootState) => state.API)
    const dispatch = useDispatch()
    React.useEffect(() => {
        if (!loading) {
            if (data) {
                const { __typename, books, ...api } = data.api
                dispatch(setApi({
                    ...api,
                    books: books.map(({ __typename, ...book }: Book) => book)
                }))
            }
            else if (error) { }
        }
    }, [data, error])
    return (
        <pre>{JSON.stringify(apiState, null, 2)}</pre>
    )
}
export default API

I have no idea how to displaying like that. Asking GPT forces me to use CSS style while I'm pretty sure the Open Library API doesn't use that way.


Solution

  • You can't do that with GQL since GQL need a POST request. It can't return json like that. But you can combine REST with your GQL.

    main.ts

    (async () => {
        await server.start()
        app.use(
            '/gql',
            cors<cors.CorsRequest>({ origin: `http://${process.env.DOMAIN}:${process.env.CLIENT_PORT}`, credentials: true }),
            express.json(),
            cp(),
            expressMiddleware(server, {
                context: async ({ req, res }) => ({ req, res })
            })
        )
        // since I saw your format URL is somewhat '/API/hash'
        app.get('/API/:hash', async (req: Request, res: Response) => {
            try {
                const userRepo = AppDataSource.getRepository(User)
                const { hash } = req.params
                const hashBuffer = Buffer.from(hash, 'hex')
                const user = await userRepo.findOne({ where: { api_key: hashBuffer } })
                if (!user) res.status(404).json({ message: 'Invalid API Key!' })
                // continue the logic
                res.status(200).json(JSON.parse(JSON.stringify('return your dict response', null, 2)))
            } catch {
            }
        }
        httpServer.listen(process.env.PORT)
    })()