Skip to content

Minimal Fastify server template with docs and client generation

Notifications You must be signed in to change notification settings

Blue9/fastify-swagger-react-query

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Barebones Fastify server with auto-generated docs (Swagger) and client (React Query).

Backend

Set up using npm install.

Run the server using npm run dev. This runs the example server in src/app.ts. Go to localhost:4000/docs to view the docs.

Generate the react query client by running npm run generateclient. Edit the location of the generated client in generate-client.ts (you'll also have to edit the generateclient script in package.json).

Usage

A server is a collection of modules, which are collections of endpoints. Every endpoint is typed using TypeBox, which type-checks all of your endpoints and is used by fastify-swagger to generate API documentation for you. Authentication is supported—just implement src/auth.ts.

Below is the example server that's run when you run npm run dev (copy of src/app.ts).

export const app = server(
  { // 1. Schemas.
    HelloRequest: Schema('HelloRequest', { recipient: Type.Optional(Type.String()) }),
    HelloResponse: Schema('HelloResponse', { hello: Type.String() }),
  },
  { // 2. Module specs.
    hello: {
      path: `/hello`,
      routes: {
        getHelloWorld: {
          auth: false,
          path: `GET /`,
          query: `HelloRequest`, // <-- Fully typed, must be a key in `schemas`
          response: `HelloResponse`,
        },
      },
    },
  },
  { // 3. Module implementations.
    hello: {
      getHelloWorld: async ({ query }) => {
        return { hello: query.recipient ?? 'World' }
      },
    },
  }
)

You can also break things up as your server grows in complexity. The moduleSpec and moduleImpl helper functions can assist with type-checking.

import { Type } from '@sinclair/typebox'
import { moduleSpec, moduleImpl, server, Schema } from './server'

// 1. Define your schemas using TypeBox
// Use Schema(TypeName, { ... }) to define each schema. This assigns an
// ID to the type which allows you to reference it in other types using
// Type.Ref(...). This also avoids duplicate type generation when
// generating the client types.
// See https://github.com/sinclairzx81/typebox#reference-types and
// https://swagger.io/docs/specification/using-ref/ for more.
const HelloRequest = Schema('HelloRequest', {
  recipient: Type.Optional(Type.String()),
})
const HelloResponse = Schema('HelloResponse', { hello: Type.String() })

const schemas = {
  HelloRequest,
  HelloResponse,
}

// 2. Define module.
const helloModuleSpec = moduleSpec(schemas, {
  path: `/hello`,
  routes: {
    getHelloWorld: {
      auth: false,
      path: `GET /`,
      query: `HelloRequest`, // <-- Fully typed, must be keyof schemas
      response: `HelloResponse`,
    },
  },
})

// 3. Implement module as a map from endpoint to handler.
// Handlers take in a typed FastifyRequest object.
const helloModuleImpl = moduleImpl(schemas, helloModuleSpec, {
  getHelloWorld: async ({ query, server, headers }) => {
    console.log(server.version, headers.host)
    return { hello: query.recipient ?? 'World' }
  },
})

// 5. Define the server spec and implementation.
const serverSpec = { hello: helloModuleSpec }
const serverImpl = { hello: helloModuleImpl }
export const app = server(schemas, serverSpec, serverImpl)

Why

Why separate the server spec (routes and types) and the implementation (route handlers)? This is mostly a personal preference, but I find that distinguising between a server's interface and its functionality makes it easier to quickly understand what a module's role is. Other developers can skim a module's spec to understand its functionality without the implementation details getting in the way.

Think of defining a server module's specification as constructing its "type," outlining the structure and features of the module. Defining the implementation is similar to defining a variable satisfying the defined type, building the concrete realization of these expectations.

About

Minimal Fastify server template with docs and client generation

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published