npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@coobaha/typed-fastify

v3.0.0

Published

opinionated types for fastify

Downloads

2,095

Readme

Typed Fastify

Build Status Coverage Status NPM Version

Online docs

This package adds strong TypeScript support to Fastify request handlers and enforces handlers to have typed schema which is used to validate request params and replies. From this schema it does two things:

  • static typechecking against TypeScript Schema
    • request.body
    • request.headers
    • request.querystring
    • request.params
    • route.path.params are also inferred and mapped to request.params, it is also not possible to make a typo in schema params
    • reply is always based on status, developer won't be able to use plain reply.send() but forced to explicitly set status first, based on which response type will be inferred
  • JSON schema generation from TS Schema (using typescript-json-schema with custom transforms, all @tjs annotations can be used to fine-tune output)
    • since we use typejescript-json-schema: all known limitations of lib are inherited:
      • Records are not transformed correctly, use { [k: string]: string } instead or hint with @tjs
  • Runtime validation using generated JSON schema (optional but strongly recommended as it brings extra safety to runtime and ensures that code assumptions about data are correct)

demo video

Usage

npm i @coobaha/typed-fastify

pnpm i @coobaha/typed-fastify

yarn add @coobaha/typed-fastify

Example of service we want to build

GET / => Hello ($querystring.name || world)

Simple implementation without schema generation will be following

import addSchema, { Schema } from '@coobaha/typed-fastify';
import fastify from 'fastify';

export interface ExampleSchema extends Schema {
  paths: {
    'GET /': {
      request: {
        querystring: {
          name?: string;
        };
      };
      response: {
        200: {
          content: string;
        };
      };
    };
  };
}

const exampleService: Service<ExampleSchema> = {
  'GET /': (req, reply) => {
    // typescript will infer correct types for us
    const name = req.query.name ?? 'World';

    // Calling send directly is not allowed
    // reply.send(`Hello ${name}`)
    // Calling send with wrong payload will result in an error
    // reply.status(200).send(new Date())

    return reply.status(200).send(`Hello ${name}`);
  },
};

const app = fastify();
addSchema(app, {
  // it is strongly recommended to generate json schema to guaruntee runtime validity
  jsonSchema: {},
  service: exampleService,
});

// Start listening.
app.listen(3000, (err: any) => {
  if (err) {
    app.log.error(err);
    process.exit(1);
  }
});

Complex examples can be found typescript tests and in integration.test.ts.

JSON schema generation

You can generate json schema from your TS types by using typed-fastify-schema or tfs bins

npx tfs gen
tfs gen [files]

Generates json schemas next to corresponding ts files

Positionals:
  files  glob pattern of files                               [string] [required]

Options:
  --help     Show help                                                 [boolean]
  --version  Show version number                                       [boolean]
# it will generate example_schema.gen.json next to file
npx tfs gen example_schema.ts

When schema is generated - just pass it to plugin to have runtime validations 🎉

import jsonSchema from './example_schema.gen.json';

// ...

addSchema(app, {
  jsonSchema,
  service,
});

Writing service

  1. Handlers in one object Type inference will work nicely in this case, you just make TS happy and things are working 🥳

  2. Handlers in a different file or separate functions - you will need to hint TS with exact type of handler.

import { RequestHandler, Schema } from '@coobaha/typed-fastify';

interface MySchema extends Schema {}

const myHandler: RequestHandler<MySchema, 'GET /hello'>['AsRoute'] = (req, reply) => {};
  1. When you want to have complex shared handler for multiple endpoints that intersect (share same props)
import { RequestHandler, Schema } from '@coobaha/typed-fastify';

interface MySchema extends Schema {}

const myHandlers: RequestHandler<MySchema, 'GET /hello' | `GET /hello2`>['AsRoute'] = (req, reply) => {};
  1. Sometimes properties won't be the same (for instance GET never has body and POST will). In this case you will probably be asked to add types to function params
import { RequestHandler, Schema } from '@coobaha/typed-fastify';

interface MySchema extends Schema {}

type MyHandlers = RequestHandler<MySchema, 'GET /hello' | `POST /hello`>;
const myHandlers = (req: MyHandlers['Request'], reply: MyHandlers['Reply']): MyHandlers['Return'] => {};

// if handler is async/await
const myHandlersAsync = async (req: MyHandlers['Request'], reply: MyHandlers['Reply']): MyHandlers['ReturnAsync'] => {};

addSchema(app, {
  jsonSchema: {},
  service: {
    'GET /hello': myHandlers,
    'GET /hello2': myHandlers,
  },
});

It might be that TS can't infer exact type of complex handler when passed to addSchema so you'll need to do it manually

addSchema(app, {
  jsonSchema: {},
  service: {
    'GET /hello': myHandlers,
    'GET /hello2': myHandlers as RequestHandler<ExtendedSchema, 'GET /hello2'>['AsRoute'],
  },
});

Annotating types

This library is using typescript-json-schema with custom transforms for schema generation. All @tjs annotations can be used to fine-tune schema output

  • @type can be used to specify end type after using toJSON, toString methods of objects like ObjectID from MongoDB

  • since we use typejescript-json-schema: all known limitations are also inherited: - Records are not transformed correctly, use { [k: string]: string } instead or hint with @tjs

  • additionalProperties are set to false by default