Hello there I’m using trpc and fastify and i have created an api end point to fetch data from prismic cms now I’m confused how to use this fetched data on my pages since I want to render my pages server side with pug template engine, when i go to /api/home I can see the data without issues, but I’m not sure how to extend my setup with trpc to allow my pages to use those fetched data
lib/trpc/index.ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
/**
* Wrapper around TRPC
*
* TRPC is a typesafe way of making an API server and a client
* The TypeScript types are shared between the two, keeping them in sync
* The strength of TRPC is how quickly you can add new endpoints
*
* @see https://trpc.io
*/
export class Trpc {
private readonly trpc = initTRPC.create({
/**
* @see https://trpc.io/docs/v10/data-transformers
*/
transformer: superjson,
});
/**
* @see https://trpc.io/docs/v10/router
*/
public readonly router = this.trpc.router;
/**
* @see https://trpc.io/docs/v10/merging-routers
*/
public readonly mergeRouters = this.trpc.mergeRouters;
/**
* @see https://trpc.io/docs/v10/procedures
**/
public readonly procedure = this.trpc.procedure;
/**
* @see https://trpc.io/docs/v10/middlewares
*/
public readonly middleware = this.trpc.middleware;
}
lib/fastify/index.ts
import type { Router } from '@trpc/server';
import { fastifyTRPCPlugin } from '@trpc/server/adapters/fastify';
import type { AnyRouterDef } from '@trpc/server/dist/core/router';
import fastify from 'fastify';
import metricsPlugin from 'fastify-metrics';
import * as trpcPlayground from 'trpc-playground/handlers/fastify';
export interface ServerOptions {
dev?: boolean;
port?: number;
prefix?: string;
}
/**
* Wrapper around fastify
*
* @see https://www.fastify.io/
*/
export class Fastify {
constructor(
/**
* The port
*/
private readonly port: number,
/**
* The host
*/
private readonly host: string,
/**
* Whether to run in development mode
* Defaults to Env.NODE_ENV === 'development'
*/
dev: boolean,
/**
* The fastify server being wrapped
*
* @dependencyinjection
*/
public readonly server = fastify({ logger: dev })
) {}
/**
* Starts the fastify server
*/
public readonly start = async () => {
try {
/**
* @see https://www.fastify.io/docs/latest/Reference/Server/#listen
*/
await this.server.listen({ port: this.port, host: this.host });
console.log('listening on port', this.port);
} catch (err) {
this.server.log.error(err);
process.exit(1);
}
};
/**
* Stop the fastify server
*/
public readonly stop = async () => {
await this.server.close();
};
/**
* Registers metrics on fastify server
*/
public readonly registerMetrics = async (endpoint = '/metrics') => {
await this.server.register(metricsPlugin, { endpoint });
};
/**
* Register a trpc router on fastify server
* Include a playground endpoint if you want to use the playground
*/
public readonly registerTrpc = async (
prefix: string,
appRouter: Router<AnyRouterDef>,
playgroundEndpoint: string | undefined
) => {
await this.server.register(fastifyTRPCPlugin, {
prefix,
trpcOptions: { router: appRouter },
});
if (playgroundEndpoint) {
this.server.register(
await trpcPlayground.getFastifyPlugin({
trpcApiEndpoint: prefix,
playgroundEndpoint,
router: appRouter,
request: {
superjson: true,
},
}),
{ prefix: playgroundEndpoint }
);
}
};
}
routes/home/index.ts
import { AbstractRoute } from '../abstract';
import { client } from '../../lib/prismic/client';
export class HomeRoute extends AbstractRoute {
name = 'home';
handler = this.trpc.procedure.query(async () => {
try {
const page = await client.getByUID('page', 'home');
if (!page) throw new Error('Home page not found');
return page;
} catch (error: any) {
throw new Error('Failed to fetch homepage: ' + error.message);
}
});
}
server.ts
import { Api } from '../api';
import { Env } from '../config/env';
import { Fastify } from '../lib/fastify';
import { Trpc } from '../lib/trpc';
/**
* The top level server that instantiates the API and starts the server
*
* @example
* const server = new Server()
* await server.start()
* await server.stop()
*/
export class Server {
constructor(
/**
* The Env options for the server
*/
private readonly env = Env.getEnv(),
/**
* The Trpc instance the API and routers will use
*
* @dependencyinjection
*/
trpc = new Trpc(),
/**
* The API instance the server will use
*
* @dependencyinjection
*/
private readonly api = new Api(trpc),
/**
* The Fastify instance the server will use to mount the API
*
* @dependencyinjection
*/
private readonly fastifyServer: Fastify | undefined = undefined
) {}
/**
* Starts the server
*/
public readonly start = async () => {
const server = await this.init();
return server.start();
};
/**
* stops the server
*/
public readonly stop = () => {
return this.fastifyServer?.stop();
};
/**
* Initializes the server if not yet initialized
*
* @returns the fastify server
*/
private readonly init = async () => {
if (this.fastifyServer) {
return this.fastifyServer;
}
const fastifyServer = new Fastify(
this.env.PORT,
this.env.HOST,
this.env.NODE_ENV === 'development'
);
await fastifyServer.registerMetrics();
await fastifyServer.registerTrpc(
this.env.TRPC_ENDPOINT,
this.api.handler,
this.env.TRPC_PLAYGROUND_ENDPOINT
);
return fastifyServer;
};
}
index.ts
import type { Api } from './api';
export { Server } from './server';
export type AppRouter = Api['handler'];
2
Answers
Since you want to call the tRPC appRouter server-side, and pass along data to your Pug templates/views, you’ll want to follow the second guide I linked in the comment: https://trpc.io/docs/server/server-side-calls
In a nutshell, it’ll look like this (code not tested):
Though you may wish to move things around based on how you want to structure the code and where/how certain things, like the
trpc
instance andappRouter
, are available.Using
tRPC
,Pug
, andFastify
allows fetching the following data from the/api/home
endpoint:Fetch data: Using tRPC, fetch the data from your HomeRoute:
Rendering with Pug Make sure Fastify is configured using point of view plus using Pug template engine:
Access Data in Pug: In the Pug template, access the data like this: