Skip to main content

Server

Create the server entrypoint, root module, and a tiny GraphQL feature.

server/src/main.ts

import "reflect-metadata";
import { Logger, ValidationPipe } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { NestFactory } from "@nestjs/core";
import {
FastifyAdapter,
type NestFastifyApplication,
} from "@nestjs/platform-fastify";
import fastifyCookie from "@fastify/cookie";
import {
readServerCorsOptions,
readServerRuntimeOptions,
summarizeServerCorsOrigin,
} from "@omgjs/labkit-server-config";
import { logStructuredEvent } from "@omgjs/labkit-server-observability";
import { AppModule } from "./app.module";

const logger = new Logger("Bootstrap");

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

await app.register(fastifyCookie);
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));

const config = app.get(ConfigService);
const cors = readServerCorsOptions(config);
const runtime = readServerRuntimeOptions(config);

app.enableCors(cors);
app.enableShutdownHooks();

logStructuredEvent(logger, "log", "server_configured", {
host: runtime.host,
port: runtime.port,
graphqlPath: runtime.graphqlPath,
corsOrigin: summarizeServerCorsOrigin(cors.origin),
corsCredentials: cors.credentials,
});

await app.listen(runtime.port, runtime.host);
}

void bootstrap().catch((error) => {
logStructuredEvent(logger, "error", "server_bootstrap_failed", {}, error);
process.exit(1);
});

server/src/app.module.ts

import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { createServerGraphqlModule } from "@omgjs/labkit-server-graphql";
import { getEnvFilePaths } from "@omgjs/labkit-server-config";
import { HelloModule } from "./hello/hello.module";

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: getEnvFilePaths(),
ignoreEnvFile: process.env.NODE_ENV === "production",
}),
HelloModule,
createServerGraphqlModule({
inject: [ConfigService],
useFactory: (configReader: ConfigService) => ({
configReader,
resolvePrincipalFromAuthorization: () => null,
}),
}),
],
})
export class AppModule {}

server/src/hello/hello.module.ts

import { Module } from "@nestjs/common";
import { HelloResolver } from "./hello.resolver";

@Module({
providers: [HelloResolver],
})
export class HelloModule {}

server/src/hello/hello.resolver.ts

import { Args, Mutation, Query, Resolver, Subscription } from "@nestjs/graphql";
import { PubSub } from "graphql-subscriptions";

const pubsub = new PubSub();
const HELLO_ADDED = "helloAdded";

@Resolver()
export class HelloResolver {
@Query(() => String)
hello(): string {
return "Hello from Labkit";
}

@Mutation(() => String)
async addHello(@Args("message") message: string): Promise<string> {
await pubsub.publish(HELLO_ADDED, { helloAdded: message });
return message;
}

@Subscription(() => String)
helloAdded() {
return pubsub.asyncIterableIterator(HELLO_ADDED);
}
}

Install the tiny subscription helper used by the example:

npm install -w server graphql-subscriptions

server/.env.development

HOST=0.0.0.0
PORT=3000
GRAPHQL_PATH=/graphql
CORS_ORIGIN=http://localhost:5173
AUTH_REFRESH_TOKEN_TRANSPORT=cookie

At this point the server demonstrates Labkit config, structured logs, and GraphQL module creation. Auth and database come next.