Skip to content

Add optional Zod/Standard Schema support for runtime validation #3802

@OskarLebuda

Description

@OskarLebuda

Description

Hi! 👋 First of all - thank you for this package. It makes working with the GitLab API much easier and the developer experience is great.

After using Gitbeaker in a few projects, I noticed that the current API responses map very cleanly to Zod or Standard Schema. Gitbeaker already has solid TypeScript definitions, so there's a good foundation to generate proper runtime validation schemas on top of them.
This could open the door to safer integrations - especially in environments where API responses need to be validated, transformed, or exposed through other internal services.

Right now developers need to maintain their own schemas manually. Adding optional built-in schemas (or a generator) would make this much simpler and reduce duplication.


Proposal

Introduce a way to use Gitbeaker with Zod or Standard Schema:

  • Provide Zod schemas generated from existing TypeScript types (either directly bundled or generated through a small build step).
  • Expose them as a separate optional module, e.g. @gitbeaker/zod or gitbeaker/schemas.
  • Keep it fully optional so core users aren't affected - but allow developers who rely on runtime validation to plug schemas in easily.
  • Ensure schemas stay in sync with the official GitLab API types. Ideally the generation would run automatically during releases.
  • If full coverage is too large for the first iteration, start with core resources (Projects, Users, Issues, Merge Requests) and expand over time.
  • And if it helps - I'm happy to contribute and assist with the implementation.

Example use cases

1. Basic API response validation

import { Gitlab } from '@gitbeaker/rest';
import { zProjectSchema, zUserSchema } from '@gitbeaker/zod';

const api = new Gitlab({ token: 'xxx' });

// Validate single project
const rawProject = await api.Projects.show(123);
const project = zProjectSchema.parse(rawProject);
// ✅ Guaranteed type safety + runtime validation

// Validate list of users
const rawUsers = await api.Users.all();
const users = zUserSchema.array().parse(rawUsers);

2. Webhook validation in API endpoints

import { zWebhookPushEventSchema, zWebhookMergeRequestEventSchema } from '@gitbeaker/zod';
import { z } from 'zod';

// Express/Next.js endpoint
app.post('/webhook/gitlab', async (req, res) => {
  const eventType = req.headers['x-gitlab-event'];
  
  try {
    if (eventType === 'Push Hook') {
      const event = zWebhookPushEventSchema.parse(req.body);
      await handlePushEvent(event);
    } else if (eventType === 'Merge Request Hook') {
      const event = zWebhookMergeRequestEventSchema.parse(req.body);
      await handleMergeRequest(event);
    }
    res.status(200).send('OK');
  } catch (error) {
    if (error instanceof z.ZodError) {
      console.error('Invalid webhook payload:', error.errors);
      res.status(400).json({ error: 'Invalid payload' });
    }
  }
});

3. Safe transformations to internal models

import { zMergeRequestSchema } from '@gitbeaker/zod';

// Transform to internal model with validation
const InternalMRSchema = zMergeRequestSchema.transform((mr) => ({
  id: mr.id,
  title: mr.title,
  author: mr.author.username,
  status: mr.state === 'merged' ? 'completed' : 'pending',
  createdAt: new Date(mr.created_at),
}));

const mrs = await api.MergeRequests.all({ projectId: 123 });
const internalMRs = mrs.map(mr => InternalMRSchema.parse(mr));

4. Integration with tRPC or other type-safe frameworks

import { zProjectSchema } from '@gitbeaker/zod';
import { initTRPC } from '@trpc/server';

const t = initTRPC.create();

export const appRouter = t.router({
  getProject: t.procedure
    .input(z.object({ id: z.number() }))
    .output(zProjectSchema)
    .query(async ({ input }) => {
      const project = await api.Projects.show(input.id);
      return zProjectSchema.parse(project);
    }),
});

Checklist

  • I have checked that this is not a duplicate issue.
  • I have read the documentation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type:featureChanges add a new feature

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions