Skip to content

Fastify Documentation

Alexander Cerutti edited this page Feb 19, 2025 · 5 revisions

This package exposes a set of typescript-ready Fastify plugins to let you focus on your business logic integration. In this document, they are organized per Apple Service.

Before using this library, a look at the Apple Documentation for Wallet webservice is suggested, in order to understand how the business logic should be developed and how data should be saved.

Plugins

Each endpoint documented by Apple in its documentation can be seen as a plugin as you might not add all of them, or add them conditionally based on the environment. They are also organized by endpoint version and by endpoint name.

This package is studied to be compatible only with ESM. It exposes the following entry points in package.json:

{
	".": "./lib/index.js",
	"./v1": "./lib/plugins/v1/index.js",
	"./v1/*": "./lib/plugins/v1/*"
}

Therefore they can be imported in the following ways:

import { v1 } from "fastify-passkit-webservice";
import { ... } from "fastify-passkit-webservice/v1";
import logPlugin from "fastify-passkit-webservice/v1/log.js";

and can be added to Fastify in the following way, through the direct entry point:

fastifyInstance.register(import("fastify-passkit-webservice/v1/registration.js"), {
   ...pluginOpts
});

Each plugin accepts some mandatory callbacks. If not provided, the plugin will throw a HandlerNotFoundError.

Validation and authentication

Some endpoints require, by Apple specification, a token to be set in the pass.json file inside the .pkpass bundle. In order to let you validate your own token, some plugins allow you to specify an async function, that will let you implement the token validation logic.

A light validation is performed directly by the plugins by looking at the Authorization Schema for it to be ApplePass.

Using with NestJS

This package is fully compatible with a NestJS application that uses @nestjs/platform-fastify. Plugins can be imported as specified above in the src/main.ts file, in the boostrap() function, where the app gets created.

However, the plugin signature might conflict as NestJS still uses fastify@v4 while this package uses fastify@v5. It is fully safe, in this case, to use // @ts-ignore as showed below. You will still have access to full intellisense auto-completion. The suggestion is to import on top the plugin in order to reduce noise.

/** src/main.ts **/

import LogPlugin from "fastify-passkit-webservice/v1/log.js";

async function bootstrap() {
	const app = await NestFactory.create<NestFastifyApplication>(
		AppModule,
		new FastifyAdapter(),
	);

	app.register(
		// @ts-ignore
		LogPlugin,
		{
			onIncomingLogs(logs) {
				/** Your implementation */
			},
		},
	);

	await app.listen(process.env.PORT ?? 3000);
}

Registration

References:

Entrypoint: fastify-passkit-webservice/v1/registration.js.

Description:

This plugin wraps both device registration and unregistration endpoints as they are coupled.

Default exports: registrationPlugin

declare function registrationPlugin(...);
export default registrationPlugin;

Plugin callbacks:

  • onRegister (mandatory)
onRegister(deviceLibraryIdentifier: string, passTypeIdentifier: string, serialNumber: string, pushToken: string): PromiseLike<boolean>;
  • onUnregister (mandatory)
onUnregister(deviceLibraryIdentifier: string, passTypeIdentifier: string, serialNumber: string): PromiseLike<boolean>;
  • tokenVerifier (optional)

Can be used to verify the token sent by Apple Wallet and that is available in a pass. If set, this function is called before both onRegister and onUnregister.

tokenVerifier?(token: string): PromiseLike<boolean>;

Update

References:

Entrypoint: fastify-passkit-webservice/v1/update.js.

Description:

This plugin wraps the request for a Pass update made by Apple Wallet on two occasions:

  • Manual refresh by the user
  • Refresh by application following an APNS notification by your server and the list endpoint invocation (see below for the plugin).

Default exports: updatePlugin

declare function updatePlugin(...);
export default updatePlugin;

Plugin callbacks:

  • onUpdateRequest (mandatory)

Generate your pass when this callback is received. You can use whatever system you want to generate a pass, but you might find passkit-generator interesting for this.

Returning undefined will make the call to return HTTP 304, as per Apple (legacy) documentation. Otherwise, HTTP 200 will be returned.

Returning something else, except undefined and Uint8Array will make the call to return HTTP 500.

onUpdateRequest(passTypeIdentifier: string, serialNumber: string): PromiseLike<Uint8Array>;
  • tokenVerifier (optional)

Can be used to verify the token sent by Apple Wallet and that is available in a pass. If set, this function is called before both onUpdateRequest.

tokenVerifier?(token: string): PromiseLike<boolean>;

List

References:

Entrypoint: fastify-passkit-webservice/v1/list.js.

Description:

This plugin wraps the request for changed passes that Apple Wallet sends after receiving an APNS notification. After the list of updated serial numbers is provided, Apple Wallet will use that information to perform a request per serial number to your update plugin.

Default exports: listPlugin

declare function listPlugin<LastUpdatedFormat = unknown>(...);
export default listPlugin;

Plugin callbacks:

  • onListRetrieve (mandatory)

This endpoint is called with two parameters and a query string: passesUpdatedSince. This value is the value you sent in the previous list request (won't be provided, therefore, on the first request).

As the format is not enforced by Apple specifications, for Typescript LastUpdatedFormat is set to be unknown and can be specified in the plugin signature or casted when you use the function.

onListRetrieve(deviceLibraryIdentifier: string, passTypeIdentifier: string, filters: {
	passesUpdatedSince?: LastUpdatedFormat
}): PromiseLike<SerialNumbers | undefined>;

Log

References:

Entrypoint: fastify-passkit-webservice/v1/log.js.

Description:

This plugin wraps the request Apple will perform in case of logs. Logs may contain records about failed requests (e.g. missing endpoints or bad status code).

Default exports: logPlugin

declare function logPlugin(...);
export default logPlugin;

Plugin callbacks:

  • onIncomingLogs (mandatory)
onIncomingLogs(logs: string[]): void;