Real-World Examples

Learn by Example
Copy & Paste Code

Production-ready examples you can copy directly into your project.

Basic CRUD API

Complete REST API with tRPC, Prisma, and Zod validation for a blog application.

tRPCPrismaZod
// routers/post.router.ts
import { createTRPCRouter, publicProcedure } from '@natiwo/api';
import { z } from 'zod';
import { db } from '../database';

export const postRouter = createTRPCRouter({
  list: publicProcedure
    .input(z.object({
      limit: z.number().min(1).max(100).default(10),
      cursor: z.string().optional(),
    }))
    .query(async ({ input }) => {
      const posts = await db.post.findMany({
        take: input.limit + 1,
        cursor: input.cursor ? { id: input.cursor } : undefined,
        orderBy: { createdAt: 'desc' },
      });

      const hasNextPage = posts.length > input.limit;
      const items = hasNextPage ? posts.slice(0, -1) : posts;

      return {
        items,
        nextCursor: hasNextPage ? items[items.length - 1].id : undefined,
      };
    }),

  create: publicProcedure
    .input(z.object({
      title: z.string().min(1).max(200),
      content: z.string().min(1),
    }))
    .mutation(async ({ input }) => {
      return db.post.create({
        data: input,
      });
    }),
});

Authentication with JWT

User authentication with JWT tokens, password hashing, and role-based access control.

JWTRBACAuth
// routers/auth.router.ts
import { createTRPCRouter, publicProcedure } from '@natiwo/api';
import { JWTManager } from '@natiwo/auth';
import { hashPassword, verifyPassword } from '@natiwo/crypto';
import { z } from 'zod';
import { db } from '../database';

const jwt = new JWTManager({
  secret: process.env.JWT_SECRET!,
  expiresIn: '7d',
});

export const authRouter = createTRPCRouter({
  register: publicProcedure
    .input(z.object({
      email: z.string().email(),
      password: z.string().min(8),
      name: z.string(),
    }))
    .mutation(async ({ input }) => {
      const hashedPassword = await hashPassword(input.password);

      const user = await db.user.create({
        data: {
          email: input.email,
          password: hashedPassword,
          name: input.name,
        },
      });

      const token = await jwt.sign({
        userId: user.id,
        role: 'user'
      });

      return { token, user };
    }),

  login: publicProcedure
    .input(z.object({
      email: z.string().email(),
      password: z.string(),
    }))
    .mutation(async ({ input }) => {
      const user = await db.user.findUnique({
        where: { email: input.email },
      });

      if (!user) {
        throw new Error('Invalid credentials');
      }

      const valid = await verifyPassword(input.password, user.password);

      if (!valid) {
        throw new Error('Invalid credentials');
      }

      const token = await jwt.sign({
        userId: user.id,
        role: user.role
      });

      return { token, user };
    }),
});

File Upload to S3

Upload files to S3 with presigned URLs, streaming, and automatic cleanup.

S3UploadStreaming
// routers/upload.router.ts
import { createTRPCRouter, protectedProcedure } from '@natiwo/api';
import { S3Storage } from '@natiwo/storage';
import { z } from 'zod';

const storage = new S3Storage({
  bucket: process.env.S3_BUCKET!,
  region: process.env.AWS_REGION!,
});

export const uploadRouter = createTRPCRouter({
  getUploadUrl: protectedProcedure
    .input(z.object({
      filename: z.string(),
      contentType: z.string(),
    }))
    .mutation(async ({ input }) => {
      const key = `uploads/${Date.now()}-${input.filename}`;

      const uploadUrl = await storage.getPresignedUrl({
        key,
        operation: 'put',
        contentType: input.contentType,
        expiresIn: 3600,
      });

      return { uploadUrl, key };
    }),

  confirmUpload: protectedProcedure
    .input(z.object({
      key: z.string(),
    }))
    .mutation(async ({ input, ctx }) => {
      const exists = await storage.exists(input.key);

      if (!exists) {
        throw new Error('File not found');
      }

      // Save to database
      const file = await db.file.create({
        data: {
          key: input.key,
          userId: ctx.user.id,
          url: storage.getPublicUrl(input.key),
        },
      });

      return file;
    }),
});

Background Job Processing

Process background jobs with BullMQ, retries, and progress tracking.

BullMQJobsQueue
// jobs/email.processor.ts
import { QueueProcess } from '@natiwo/queue';
import { sendEmail } from '../lib/email';

export class EmailProcessor {
  @QueueProcess('email', 'send')
  async sendEmail(job: { data: { to: string; subject: string; body: string } }) {
    const { to, subject, body } = job.data;

    await sendEmail({
      to,
      subject,
      body,
    });

    return { sent: true };
  }

  @QueueProcess('email', 'batch')
  async sendBatchEmail(job: { data: { users: Array<{ email: string }> } }) {
    const { users } = job.data;
    const total = users.length;

    for (let i = 0; i < users.length; i++) {
      await sendEmail({
        to: users[i].email,
        subject: 'Newsletter',
        body: 'Welcome!',
      });

      // Update progress
      await job.updateProgress(Math.round(((i + 1) / total) * 100));
    }

    return { sent: total };
  }
}

// Usage in router
import { createTRPCRouter, protectedProcedure } from '@natiwo/api';
import { QueueService } from '@natiwo/queue';

export const emailRouter = createTRPCRouter({
  send: protectedProcedure
    .input(z.object({
      to: z.string().email(),
      subject: z.string(),
      body: z.string(),
    }))
    .mutation(async ({ input }) => {
      const queue = new QueueService('email');

      const job = await queue.add('send', input, {
        attempts: 3,
        backoff: {
          type: 'exponential',
          delay: 2000,
        },
      });

      return { jobId: job.id };
    }),
});

Observability Setup

Complete observability with logging, metrics, APM, and error tracking.

LoggingMetricsAPM
// lib/observability.ts
import { createLogger } from '@natiwo/observability';
import { MetricsCollector } from '@natiwo/observability';
import { SentryErrorTracker } from '@natiwo/observability';

// Logger
export const logger = createLogger('winston', {
  level: process.env.LOG_LEVEL || 'info',
  format: 'json',
  transports: [
    { type: 'console' },
    { type: 'file', filename: 'app.log' },
  ],
  metadata: {
    service: 'api',
    environment: process.env.NODE_ENV,
  },
});

// Metrics
export const metrics = new MetricsCollector({
  prefix: 'natiwo_api',
  exporters: ['prometheus'],
});

// Error Tracking
export const errorTracker = new SentryErrorTracker({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
});

// Usage in middleware
export async function requestLogger(req, res, next) {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;

    logger.info('HTTP Request', {
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration,
    });

    metrics.histogram('http_request_duration', duration, {
      method: req.method,
      status: res.statusCode,
    });
  });

  next();
}

More Examples on GitHub

Check out our GitHub repository for complete example projects and templates

View on GitHub