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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lokalise/fastify-api-contracts

v5.3.1

Published

This package adds support for generating fastify routes using universal API contracts, created with `@lokalise/api-contracts`

Readme

API contract support for fastify

This package adds support for generating fastify routes using universal API contracts, created with @lokalise/api-contracts

This module requires fastify-type-provider-zod type provider to work and is ESM-only.

Usage

Basic usage pattern using the unified buildFastifyRoute builder:

import { buildFastifyRoute, buildFastifyRouteHandler, injectByContract } from '@lokalise/fastify-api-contracts'
import { buildRestContract } from '@lokalise/api-contracts'
import {
    type ZodTypeProvider,
    serializerCompiler,
    validatorCompiler,
} from 'fastify-type-provider-zod'

// GET route
const getContract = buildRestContract({
    method: 'get',
    successResponseBodySchema: RESPONSE_BODY_SCHEMA,
    requestPathParamsSchema: REQUEST_PATH_PARAMS_SCHEMA,
    requestQuerySchema: REQUEST_QUERY_SCHEMA,
    requestHeaderSchema: REQUEST_HEADER_SCHEMA,
    pathResolver: (pathParams) => `/users/${pathParams.userId}`,
})

// POST route
const postContract = buildRestContract({
    method: 'post',
    successResponseBodySchema: RESPONSE_BODY_SCHEMA,
    requestBodySchema: REQUEST_BODY_SCHEMA,
    requestPathParamsSchema: REQUEST_PATH_PARAMS_SCHEMA,
    pathResolver: (pathParams) => `/users/${pathParams.userId}`,
})

// DELETE route
const deleteContract = buildRestContract({
    method: 'delete',
    successResponseBodySchema: z.undefined(),
    requestPathParamsSchema: REQUEST_PATH_PARAMS_SCHEMA,
    pathResolver: (pathParams) => `/users/${pathParams.userId}`,
})

// buildFastifyRoute automatically infers the correct handler type from the contract:
// - GET/DELETE contracts -> handler without req.body
// - POST/PUT/PATCH contracts -> handler with req.body
const getRoute = buildFastifyRoute(getContract, (req) => {
    // req.query, req.params and req.headers are typed from the contract
}, (contractMetadata) => {
    // Optionally, use this callback to dynamically add extra Fastify route options
    // (like config, preHandler, etc.) using contract metadata.
})

const postRoute = buildFastifyRoute(postContract, (req) => {
    // req.body, req.query, req.params and req.headers are typed from the contract
})

const deleteRoute = buildFastifyRoute(deleteContract, (req) => {
    // req.params is typed from the contract, no req.body
})

const app = fastify({
    // app params
})

app.setValidatorCompiler(validatorCompiler)
app.setSerializerCompiler(serializerCompiler)

app.withTypeProvider<ZodTypeProvider>().route(getRoute)
app.withTypeProvider<ZodTypeProvider>().route(postRoute)
app.withTypeProvider<ZodTypeProvider>().route(deleteRoute)

await app.ready()

// used in tests, you can use '@lokalise/frontend-http-client' in production code
// injectByContract automatically determines the HTTP method from the contract
const postResponse = await injectByContract(app, postContract, {
    pathParams: { userId: '1' },
    body: { id: '2' },
    headers: { authorization: 'some-value' } // can be passed directly
})

// used in tests, you can use '@lokalise/frontend-http-client' in production code
const getResponse = await injectByContract(app, getContract, {
    pathParams: { userId: '1' },
    headers: async () => ({ authorization: 'some-value' }) // headers producing function (sync or async) can be passed as well
})

Separating handler from route definition

Use buildFastifyRouteHandler to define the handler separately from the route. It works with any contract type (GET, DELETE, POST, PUT, PATCH):

import {
    buildFastifyRoute,
    buildFastifyRouteHandler
} from '@lokalise/fastify-api-contracts'
import { buildRestContract } from '@lokalise/api-contracts'

const contract = buildRestContract({
    method: 'post',
    requestBodySchema: REQUEST_BODY_SCHEMA,
    successResponseBodySchema: BODY_SCHEMA,
    requestPathParamsSchema: PATH_PARAMS_SCHEMA,
    pathResolver: (pathParams) => `/users/${pathParams.userId}`,
})

const handler = buildFastifyRouteHandler(contract,
    async (req, reply) => {
        // handler definition here, req and reply will be correctly typed based on the contract
    }
)

const routes = [
    buildFastifyRoute(contract, handler),
]

Accessing the contract

In case you need some of the contract data within your lifecycle hook or a handler, it is exposed as a part of a route config, and can be accessed like this:

const route = buildFastifyRoute(contract, (req) => {
    const { apiContract } = req.routeOptions.config
})

Deprecated APIs

The following functions are deprecated and will be removed in a future version. Use the unified replacements instead:

| Deprecated | Replacement | |---|---| | buildFastifyNoPayloadRoute | buildFastifyRoute | | buildFastifyNoPayloadRouteHandler | buildFastifyRouteHandler | | buildFastifyPayloadRoute | buildFastifyRoute | | buildFastifyPayloadRouteHandler | buildFastifyRouteHandler | | injectGet | injectByContract | | injectDelete | injectByContract | | injectPost | injectByContract | | injectPut | injectByContract | | injectPatch | injectByContract |