import fastify from 'fastify'; import { Type, Static } from '@sinclair/typebox'; import { TypeBoxValidatorCompiler } from '@fastify/type-provider-typebox'; // Create Fastify instance const app = fastify({ logger: true }); // Set up TypeBox validator app.setValidatorCompiler(TypeBoxValidatorCompiler); // Define TypeBox schema as the single source of truth const UserSchema = Type.Object({ id: Type.Number(), name: Type.String(), email: Type.String(), age: Type.Number({ minimum: 0 }) }); // Derive TypeScript type from the TypeBox schema type User = Static; // Response schema for better documentation const ResponseSchema = Type.Object({ user: UserSchema, canDrink: Type.Boolean(), greeting: Type.String() }); // Main function to set up the server async function startServer() { // Register Swagger plugin using dynamic import await app.register(import('@fastify/swagger'), { swagger: { info: { title: 'User API', description: 'API for user operations with TypeBox validation', version: '1.0.0' }, tags: [ { name: 'users', description: 'User related endpoints' } ] } }); // Register Swagger UI plugin using dynamic import await app.register(import('@fastify/swagger-ui'), { routePrefix: '/swagger', uiConfig: { docExpansion: 'list', deepLinking: false }, staticCSP: true }); // Register our route app.post('/api/users', { schema: { description: 'Create a new user', tags: ['users'], body: UserSchema, response: { 200: ResponseSchema } } }, async (request, reply) => { const user = request.body as User; const canDrink = user.age >= 21; const greeting = `Hello ${user.name.toUpperCase()}, your email ${user.email.toLowerCase()} has been recorded`; return { user, canDrink, greeting }; }); // Wait until all plugins are ready await app.ready(); // Start the server try { await app.listen({ port: 3000 }); console.log('Server running at http://localhost:3000'); console.log('Swagger UI available at: http://localhost:3000/swagger'); } catch (err) { app.log.error(err); process.exit(1); } } // Start the server startServer();