Return to Blog Home

Media Quotes TypeScript & React

Source Live

Media Quotes TypeScript & React

Leveraging the OpenAPI ecosystem for us!

While the frontend of the Media Quotes API isn't the goal, it can serve as an example to those that want to use my API in their own serious projects on how to do so.

TypeScript

First was the fun experience of converting the application to TypeScript - starting off as every conversion goes:

npx tsc --init

I then customized the tsconfig.json slightly, setting include to "src/**/*.ts", the outDir to "dist", and enabling both sourceMap and inlineSources.

From that point on it was the traditional process of renaming files from .js to .ts, and then fixing the errors that TypeScript was throwing - which were quite seamless due to the fact that I was using JSDoc comments for the most part.

openapi-typescript-codegen

One of the various Code Generation toolos in the OpenAPI ecosystem is openapi-typescript-codegen - point it at an OpenAPI spec and it will generate a fully usable TypeScript client, which is exactly what I wanted to demonstrate.

Of course my OpenAPI spec was generated by the server, so with the help of start-server-and-test:

    "build:generate-frontend-types": "npx openapi-typescript-codegen --input http://localhost:1338/api-docs.json --output frontend/src/openapi --useOptions  --useUnionTypes --name MediaQuotesClient",
    "build:frontend-types": "start-server-and-test 'PORT=1338 <abbr title="Node Package Manager"><a href="https://www.npmjs.com/">` npm`</a></abbr> run start' http-get://localhost:1338/api-docs.json 'npm run build:generate-frontend-types'",

I ws able to start up the server, generate the client, then shut down the server all ine one command.

A commonly-overlooked result of using these code generation tools - especially typed ones - is that it may reveal inaccuracies in your OpenAPI spec, which is exactly what happened here, highlighting a number of fields that were not marked as required, or were totaly absent from the spec.

React

Now that we have a TypeScript client, we can use it in our React application - of which I choose vite as the build tool, as it's both fast and easy to use.

As this isn't a sole frontend application, I did have to tell Vite to proxy my local requests to the real backend, which singe they all existed under the /api path was quite easy in vite.config.ts:

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:1337',
        changeOrigin: true,
      }
    }
  }
})

From there everything was normal React usage, with the only unique thing being that the generated client was used to make the requests - of which returned CancelablePromise objects, which with the quick crafting of a custom hook I was able to integrate into my variious useEffect() usages accordingly.

The last unique thing about the React frontend compared to the previous static EJS one was that the URL query parameters that allowed returning to this page were being updated as the user typed, fuly syncronized with the state of the application with the help of another custom hook useSearchState().

Now that the fullstack application had some additional build steps, it did require quite an expansion to my existing package.json scripts - in addition to the type-related ones shown earlier:

{
  "build": "npm run build:backend && <abbr title="Node Package Manager"><a href="https://www.npmjs.com/">` npm`</a></abbr> run build:frontend",
  "build:backend": "tsc",
  "build:frontend-react": "npm --prefix frontend run build",
  "build:frontend": "npm run build:frontend-types && <abbr title="Node Package Manager"><a href="https://www.npmjs.com/">` npm`</a></abbr> run build:frontend-react",
  "start": "node dist/index.js",
  "dev": "concurrently 'npm run build:backend -- --watch' 'nodemon --watch dist/api --exec \"npm run build:frontend-types\"' 'nodemon --watch dist --exec \"ts-node src/index.ts\"' 'wait-on http-get://localhost:1337/api/status && <abbr title="Node Package Manager"><a href="https://www.npmjs.com/">` npm`</a></abbr> --prefix frontend run dev'",
  "postinstall": "npm --prefix frontend install"
}

With the build script being the root that eventually builds both the backend first, then the frontend - which builds the types client first, then the react application.

Of course since we now use multiple package.json, we needed a postinstall that would npm install each dependant package.json.

Lastly - and commonly the most complicated - is the dev script, which is responsible for watching the backend, the frontend client, and the frontend itself!

Conclusion

While I didn't remote the EJS frontend - only moved it under the /eht path - now the API has a real modern React example to show how one can use it - and technically any OpenAPI spec - in their own projects.